"""
des: QuickReName 主界面
author: mr_52hz
date: 2021-01-28
"""
import os
import sys
import time

from PyQt5.QtCore import Qt, QCoreApplication, QUrl, QObject, pyqtSignal, QThread
from PyQt5.QtGui import QIcon, QFont, QDesktopServices, QCloseEvent
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, qApp, QPushButton, QLabel, \
    QWidget, QFrame, QGridLayout, QComboBox, QTextEdit, QFileDialog, QDesktopWidget

import settings
from ReNameHandler import ReNameFuncMixin
from UI.help import HelpWidget, AboutWidget
from UI.inputArea import InputWidget
from UI.qssReader import QssReader


class QuickRenameMixin(object):

    def __init__(self, *args, **kwargs):
        pass

    def get_xy(self, _window='first'):
        # first一级主界面
        # second 二级界面相对于主界面
        if _window == 'first':
            w, h = self.get_screen_wh()
            return (w - settings.MIN_WINDOW_WIDTH) / 2, (h - settings.MIN_WINDOW_HEIGHT) / 2
        elif _window == 'second':
            return self.x() + (self.width() - 400) / 2, self.y() + (self.height() - 200) / 2

    @staticmethod
    def get_screen_wh():
        """
        只能在APP初始化之后调用
        :return:
        """
        desktop = QDesktopWidget()
        screen_width = desktop.screenGeometry().width()
        screen_height = desktop.screenGeometry().height()
        return screen_width, screen_height

    @staticmethod
    def _getActionMenu(win, name, shortcut, tip, callback, isCheckable=False):
        menu = QAction(QIcon(), name, win)
        menu.setShortcut(shortcut)
        menu.setStatusTip(tip)
        menu.triggered.connect(callback)
        if isCheckable:
            menu.setCheckable(True)
        return menu


class QuickReName(QMainWindow, QuickRenameMixin):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.icon_path = kwargs.get('icon', settings.BASE_DIR + '/UI/qss/icon.png')
        self.light_qss = kwargs.get('light', settings.BASE_DIR + '/UI/qss/main-light.qss')
        self.night_qss = kwargs.get('night', settings.BASE_DIR + '/UI/qss/main-night.qss')
        self.main_widget = QWidget()
        self.main_widget.setObjectName('main-widget')
        self.main_status_bar = self.statusBar()
        self.total_num_status_bar_label = QLabel(' Total: Null ')
        self.total_num_status_bar_label.setObjectName('status_msg')
        self.rename_num_status_bar_label = QLabel(' RnNum: Null ')
        self.rename_num_status_bar_label.setObjectName('status_msg')
        self.path_msg_label = QLabel()
        self.path_msg_label.setObjectName('path_msg')
        self.rename_func_bx = QComboBox()
        self.rename_input_widget = InputWidget('', self)
        self.preview_text_edit = QTextEdit()
        self.run_btn = QPushButton()
        self.preview_btn = QPushButton()
        self.help_widget = None
        self.about_widget = None
        self.is_loading = False
        self.is_running = False
        self.is_path_ok = False
        self.is_func_params_ok = False
        self.is_previewed = False
        self.is_ran = False
        self.total_nums = 0
        self.qss_reader = QssReader('')
        self.initWindow()
        self.loadTheme()
        self.initThread()

    def loadTheme(self):
        is_night = settings.q_st.value('theme/night', 'off')
        if is_night == 'on':
            self.qss_reader.changeQss(self.night_qss, qApp)
            self.night_act.setChecked(True)
        elif is_night == 'off':
            self.qss_reader.changeQss(self.light_qss, qApp)
            self.night_act.setChecked(False)

        self._setFont()

    @staticmethod
    def _setFont():
        font_size = settings.q_st.value('theme/font-size', 11)
        app_font = QFont("Microsoft YaHei", int(font_size))
        qApp.setFont(app_font)

    def initWindow(self):
        # 添加菜单栏 状态栏
        self.addMenu()
        self.main_status_bar.addPermanentWidget(self.rename_num_status_bar_label, stretch=0)
        self.main_status_bar.addPermanentWidget(self.total_num_status_bar_label, stretch=0)
        self.main_status_bar.showMessage('design by Mr-52Hz')
        self.setWindowTitle(settings.QUICK_RENAME_NAME)
        self.setWindowIcon(QIcon(self.icon_path))
        # 设置启动主窗口位置为(200, 200)处，并且宽高为settings
        cache_window_geometry = settings.q_st.value('theme/window', None)
        if cache_window_geometry is None:
            x, y = self.get_xy(_window='first')
            self.setGeometry(x, y, settings.MIN_WINDOW_WIDTH, settings.MIN_WINDOW_HEIGHT)
        else:
            self.setGeometry(cache_window_geometry)
        self.setMinimumSize(settings.MIN_WINDOW_WIDTH, settings.MIN_WINDOW_HEIGHT)
        # 工作区主窗口
        self.initWorkArea()
        self.show()

    def initThread(self):
        self.thread = QThread()
        self.rename_delay_task = RenameDelayTask()
        self.rename_delay_task.moveToThread(self.thread)
        # main trans params to Thread
        self.rename_delay_task.path_changed_signal.connect(self.rename_delay_task.pathChangeEvent)
        self.rename_delay_task.start_preview_signal.connect(self.rename_delay_task.startPreview)
        self.rename_delay_task.ensure_func_signal.connect(self.rename_delay_task.ensureFunc)
        self.rename_delay_task.run_rename_signal.connect(self.rename_delay_task.runRename)
        # Thread callback
        self.rename_delay_task.is_func_legal_signal.connect(self.isFuncStrLegalEvent)
        self.rename_delay_task.is_path_legal_signal.connect(self.isPathLegalEvent)
        self.rename_delay_task.line_to_preview_signal.connect(self.appendPreviewMsg)
        self.rename_delay_task.done_signal.connect(self.previewOrRunDoneEvent)
        self.thread.start()

    def initWorkArea(self):
        # 设置中心窗口
        self.setCentralWidget(self.main_widget)
        main_layout = QGridLayout()
        main_layout.setSpacing(10)

        # 路径选择按钮
        choice_dir_btn = QPushButton()
        choice_dir_btn.setFixedSize(settings.BTN_WIDTH, settings.BTN_HEIGHT)
        choice_dir_btn.setText('选择目录')
        choice_dir_btn.clicked.connect(self.choiceDirectory)

        # 路径展示框
        self.path_msg_label.setMinimumSize(settings.PATH_LABEL_WIDTH, settings.PATH_LABEL_HEIGHT)
        self.path_msg_label.setText(' Please choose your rename files directory.')
        # 设置边框
        self.path_msg_label.setFrameShape(QFrame.Box)
        self.path_msg_label.setProperty('level', 'init')
        self.updatePathMsgLabel()

        # 功能选择框
        self.rename_func_bx.addItems(settings.RENAME_FUNCS.keys())
        self.rename_func_bx.setFixedSize(settings.BTN_WIDTH, settings.BTN_HEIGHT)
        self.rename_func_bx.currentIndexChanged[str].connect(self.renameFuncChanged)

        self.rename_input_widget.is_input_ready_signal[bool].connect(self.renameInputEdited)

        self.preview_btn.setFixedSize(settings.BTN_WIDTH, settings.BTN_HEIGHT)
        self.preview_btn.setMinimumSize(settings.BTN_WIDTH, settings.BTN_HEIGHT)
        self.preview_btn.clicked.connect(self.previewClicked)
        self.preview_btn.setText('预览')
        self.preview_btn.setDisabled(True)

        # 预览框禁止点击 横、纵向 滑动
        self.preview_text_edit.setReadOnly(True)
        self.preview_text_edit.setMinimumSize(settings.PATH_LABEL_WIDTH, settings.PATH_LABEL_HEIGHT * 5)
        self.preview_text_edit.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.preview_text_edit.setLineWrapMode(QTextEdit.NoWrap)

        # 执行按钮

        self.run_btn.setFixedSize(settings.BTN_WIDTH, settings.BTN_HEIGHT)
        self.run_btn.clicked.connect(self.runClicked)
        self.run_btn.setText('执行')
        self.run_btn.setDisabled(True)

        # 第一行
        main_layout.addWidget(choice_dir_btn, 0, 0, 1, 1)
        main_layout.addWidget(self.path_msg_label, 0, 1, 1, 4)
        # 第二 三行
        main_layout.addWidget(self.rename_func_bx, 1, 0, 2, 1)
        main_layout.addWidget(self.rename_input_widget, 1, 1, 2, 4)
        # 第 4 7行
        main_layout.addWidget(self.preview_btn, 3, 0, 4, 1)
        main_layout.addWidget(self.preview_text_edit, 3, 1, 4, 4)

        main_layout.addWidget(self.run_btn, 7, 1, 1, 3)
        main_layout.setRowStretch(4, 1)
        self.main_widget.setLayout(main_layout)
        choice_dir_btn.setFocus(True)

    def addMenu(self):
        # 文件
        # 打开
        open_act = self._getActionMenu(self, '打开(&O)', 'Ctrl+O', '打开', self.choiceDirectory)
        # 打开资源管理器
        show_in_explorer_act = self._getActionMenu(self, 'show', 'Ctrl+E', 'show', self.showInExplorer)
        # 退出动作
        exit_act = self._getActionMenu(self, '退出(&E)', 'Ctrl+Q', '退出程序', self.quit)

        # 视图
        # 恢复
        reload_act = self._getActionMenu(self, '重置', 'Ctrl+Shift+R', '恢复默认布局', self.reloadClicked)
        recovery_preview = self._getActionMenu(self, '撤销预览', 'Ctrl+R', '恢复文件预览', self.recoveryPreview)
        self.night_act = self._getActionMenu(self, 'Night', 'Ctrl+N', '深色主题', self.themeNightEvent, True)

        # 帮助
        # help
        help_act = self._getActionMenu(self, '帮助(&H)', 'Ctrl+H', '帮助', self.helpClicked)
        # about
        about_act = self._getActionMenu(self, '关于(&A)', 'Ctrl+A', '关于', self.aboutClicked)

        # 菜单栏-文件
        main_menu = self.menuBar()
        _main = main_menu.addMenu('文件(&F)')
        _main.addAction(open_act)
        _main.addAction(show_in_explorer_act)
        _main.addAction(exit_act)

        # 菜单栏-窗口
        view_menu = self.menuBar()
        _window = view_menu.addMenu('视图(&V)')
        _window.addAction(self.night_act)
        _window.addAction(recovery_preview)
        _window.addAction(reload_act)

        # 菜单栏-帮助
        help_menu = self.menuBar()
        _help = help_menu.addMenu('帮助(&H)')
        _help.addAction(help_act)
        _help.addAction(about_act)

    def choiceDirectory(self):

        frame = QFileDialog.getExistingDirectory(self, '打开文件夹', settings.DOCUMENTS_DIR)
        if frame:
            self.rename_delay_task.path_changed_signal.emit(frame)

    def isPathLegalEvent(self, is_ok, path, html, total):
        self.is_path_ok = is_ok
        if self.is_path_ok:
            self.path_msg_label.setText(path)
            self.path_msg_label.setProperty('level', 'success')
            self.updatePathMsgLabel()
            self.total_nums = total
            self.total_num_status_bar_label.setText(' Total: %s ' % total)
            self.preview_text_edit.clear()
            self.rename_delay_task.start_preview_signal.emit('init', '')
        else:
            self.preview_text_edit.setText(html)
            self.path_msg_label.setText(path)
            self.path_msg_label.setProperty('level', 'error')
            self.updatePathMsgLabel()

        self.changeBtnStatusAndText()

    def appendPreviewMsg(self, line, current, renamed):
        self.preview_text_edit.append(line)
        if current:
            progress = '进度: %s/%s' % (current, self.total_nums)
            self.main_status_bar.showMessage(progress)
        if renamed:
            self.rename_num_status_bar_label.setText(' RnNum: %s ' % renamed)

    def showInExplorer(self):
        if self.is_path_ok:
            url = QUrl('file:///' + self.path_msg_label.text())
            QDesktopServices().openUrl(url)

    def themeNightEvent(self, status):
        if status:
            settings.q_st.setValue('theme/night', 'on')
            self.qss_reader.changeQss(self.night_qss, qApp)
        else:
            self.qss_reader.changeQss(self.light_qss, qApp)
            settings.q_st.setValue('theme/night', 'off')
        self._setFont()

    def helpClicked(self):
        x, y = self.get_xy(_window='second')
        self.help_widget = HelpWidget(x=x, y=y)
        self.help_widget.show()

    def aboutClicked(self):
        # 需要通过self实例化为全局变量，不加self的话，一运行就被回收，也就无法显示。
        x, y = self.get_xy(_window='second')
        self.about_widget = AboutWidget(x=x, y=y, icon=self.icon_path)
        self.about_widget.show()

    def reloadClicked(self):
        # 重置窗口布局
        if self.width() != settings.MIN_WINDOW_WIDTH or self.height() != settings.MIN_WINDOW_HEIGHT:
            self.resize(settings.MIN_WINDOW_WIDTH, settings.MIN_WINDOW_HEIGHT)
        # 重置预览
        self.recoveryPreview()

    def recoveryPreview(self):
        if self.is_previewed is False:
            return
        if self.is_func_params_ok and self.is_path_ok:
            if self.is_running is False and self.is_loading is False:
                self.is_ran = False
                self.is_previewed = False
                self.preview_text_edit.clear()
                self.rename_delay_task.start_preview_signal.emit('init', '')

    def renameFuncChanged(self, func):
        self.is_func_params_ok = False
        self.changeBtnStatusAndText()
        self.rename_input_widget.changeFuncWidgets(func)

    def renameInputEdited(self, status):
        if status:
            rename_func_key = self.rename_func_bx.currentText()
            func_str = settings.RENAME_FUNCS.get(rename_func_key, 'replace')
            # 首先要确认函数是否合规 包括参数
            first_text = self.rename_input_widget.first_line_edit.text()
            second_text = self.rename_input_widget.second_line_edit.text()
            self.rename_delay_task.ensure_func_signal.emit(func_str, first_text, second_text)
        else:
            self.is_func_params_ok = False
            self.changeBtnStatusAndText()

    def previewClicked(self):
        # 必须开始预览 就先改变按钮状态 等待预览结束
        self.is_previewed = True
        self.is_loading = True
        self.changeBtnStatusAndText()
        preview_text = self.preview_text_edit.toPlainText()
        self.preview_text_edit.clear()
        # 函数校验通过 开始预览
        self.rename_delay_task.start_preview_signal.emit('func', preview_text)

    def isFuncStrLegalEvent(self, is_func_ok):
        if is_func_ok:
            self.is_func_params_ok = True
            self.is_previewed = False
            self.is_ran = False
            self.changeBtnStatusAndText()

    def runClicked(self):
        # 执行重命名
        self.is_ran = True
        self.is_running = True
        self.changeBtnStatusAndText()
        preview_text = self.preview_text_edit.toPlainText()
        self.rename_delay_task.run_rename_signal.emit(preview_text)
        self.preview_text_edit.clear()

    def previewOrRunDoneEvent(self, s):
        if s == 'preview':
            self.is_loading = False
            self.changeBtnStatusAndText()
            self.main_status_bar.showMessage(' 预览结束')
        elif s == 'run':
            self.is_running = False
            self.changeBtnStatusAndText()
            self.main_status_bar.showMessage(' 执行完成')

    def changeBtnStatusAndText(self):
        # status
        if self.is_path_ok is False or self.is_func_params_ok is False or \
                self.is_loading or self.is_running:
            self.preview_btn.setDisabled(True)
            self.run_btn.setDisabled(True)
        else:
            if self.is_ran:
                self.run_btn.setDisabled(True)
            else:
                self.run_btn.setEnabled(True)
            if self.is_previewed:
                self.preview_btn.setDisabled(True)
            else:
                self.preview_btn.setEnabled(True)
        # text
        if self.is_loading:
            self.preview_btn.setText('...')
        else:
            self.preview_btn.setText('预览')

        if self.is_running:
            self.run_btn.setText('...')
        else:
            self.run_btn.setText('执行')

    def updatePathMsgLabel(self):
        self.path_msg_label.style().unpolish(self.path_msg_label)
        self.path_msg_label.style().polish(self.path_msg_label)
        self.path_msg_label.update()

    def closeEvent(self, event: QCloseEvent) -> None:
        self.quit()

    def quit(self):
        settings.q_st.setValue('theme/window', self.geometry())
        settings.q_st.setValue('author', 'mr52hz')
        # 显示调用关闭窗口
        self.close()
        # 等待写入等操作完成 quit
        qApp.quitOnLastWindowClosed()


class HtmlMixin(object):
    """
    srcFile: file
    previewFile: renameFile
    srcLine:  ▼ + srcFile
    previewLine: ➥ + previewFile
    """

    # file -> html
    @staticmethod
    def srcFileToSrcHtml(order, file):
        # 添加pre修复路径问题
        #  &#9660; ==>' ▼ ' 格式不能改动
        return '<p style="white-space:pre">%s &#9660; %s</p>' % (order, file)

    @staticmethod
    def previewFileToPreviewHtml(file, color='gray'):
        # 添加pre属性
        # &#10149; ==>'➥ ' 格式不能改动
        return '<p style="color:%s;white-space:pre;">           &#10149; %s</p>' % (color, file)

    @staticmethod
    def errorHtml(s):
        return '<p style="color:red">%s</p>' % s

    def previewErrorHtml(self, file):
        err = '路径: %s 下无文件' % file
        return self.errorHtml(err)

    def previewPathNotFound(self, path):
        err = '路径不存在: %s' % path
        return self.errorHtml(err)

    # line -> file
    @staticmethod
    def getOrderAndSrcFileFromSrcLine(line):
        if line.count(settings.SRC_SIGN) != 1:
            return '', ''
        order, file = line.split(settings.SRC_SIGN)
        return order, file

    @staticmethod
    def getPreviewFileFromPreviewLine(line):
        if line.count(settings.DESC_SIGN) != 1:
            return ''
        _, file = line.split(settings.DESC_SIGN)
        return file


class RenameDelayTask(QObject, ReNameFuncMixin, HtmlMixin):

    # main 改变路径时触发
    path_changed_signal = pyqtSignal(str)
    # 路径校验回调 (True, path, html, 0/total_nums)
    is_path_legal_signal = pyqtSignal(bool, str, str, int)
    # main 开始预览调用 (type/init, 'text')
    start_preview_signal = pyqtSignal(str, str)
    # 预览回调 (html, current, RnNum)
    line_to_preview_signal = pyqtSignal(str, str, str)
    # 校验函数是否合法(func_str, first, second)
    ensure_func_signal = pyqtSignal(str, str, str)
    is_func_legal_signal = pyqtSignal(bool)
    # 执行rename
    run_rename_signal = pyqtSignal(str)
    # 预览或者运行结束 (preview/run)
    done_signal = pyqtSignal(str)

    def __init__(self):
        super(RenameDelayTask, self).__init__()
        self.origin_files = []
        self.root = ''
        # 函数相关
        self.func = None
        self.first_param = None
        self.second_param = None
        self.delay_sec = 0

    def pathChangeEvent(self, path):
        if not os.path.exists(path):
            # path 不存在 回调路径不存在
            path_not_found_html = self.previewPathNotFound(path)
            self.is_path_legal_signal.emit(False, path, path_not_found_html, 0)
            return

        self.root = path
        self.origin_files = os.listdir(path)
        self.origin_files = [file for file in self.origin_files if os.path.isfile(os.path.join(path, file))]
        self.origin_files.sort()

        if len(self.origin_files) > 0:
            # 路径下必须有文件
            self.is_path_legal_signal.emit(True, path, '', len(self.origin_files))
        else:
            no_files_error_html = self.previewErrorHtml(path)
            self.is_path_legal_signal.emit(False, path, no_files_error_html, 0)

    def startPreview(self, preview_type='init', text=''):
        if preview_type == 'init':
            idx, rn_num = 0, ''
            for file in self.origin_files:
                idx += 1
                current = str(idx).zfill(self.line_nums_max_bit)
                preview_line_html = self.srcFileToSrcHtml(current, file)
                if idx == 1:
                    rn_num = 'Null'
                self.line_to_preview_signal.emit(preview_line_html, current, rn_num)
                time.sleep(self.delay_sec)
        elif preview_type == 'func':
            lines = text.split('\n')
            is_lines_contain_preview_line = self.is_lines_contain_preview_line(lines)
            html_text = ''
            src_file = ''
            # lines中包含预览信息line
            if is_lines_contain_preview_line:
                idx, diff = 0, 0
                for line in lines:
                    if settings.SRC_SIGN in line:
                        html_text = ''
                        src_file, src_line_html = self.srcLineToSrcHtml(line)
                        html_text += src_line_html
                    elif settings.DESC_SIGN in line:
                        # 继续上次修改的名字再修改
                        idx += 1
                        current = str(idx).zfill(self.line_nums_max_bit)
                        is_diff, html_text_2 = self.previewLineToPreviewHtml(line, src_file)
                        html_text += html_text_2
                        if is_diff:
                            diff += 1
                        re_num = str(diff).zfill(self.line_nums_max_bit)
                        self.line_to_preview_signal.emit(html_text, current, re_num)
                        time.sleep(self.delay_sec)
                        html_text = ''
            # lines中无预览信息line
            else:
                idx, diff, rn_num = 0, 0, ''
                for line in lines:
                    idx += 1
                    if settings.SRC_SIGN in line:
                        # 1、按行解析生成 源文件的line<html>信息
                        src_file, src_line_html = self.srcLineToSrcHtml(line)
                        html_text += src_line_html
                        # 2、同时生成函数执行func预览line<html>信息
                        is_diff, html_text_2 = self.srcLineToPreviewHtml(line)
                        html_text += html_text_2
                        if is_diff:
                            diff += 1
                            rn_num = str(diff).zfill(self.line_nums_max_bit)
                        current = str(idx).zfill(self.line_nums_max_bit)
                        self.line_to_preview_signal.emit(html_text, current, rn_num)
                        time.sleep(self.delay_sec)
                        html_text = ''
        self.done_signal.emit('preview')

    def ensureFunc(self, func_str, first, second):
        if hasattr(self, func_str):
            self.func = getattr(self, func_str)
            self.first_param = first
            self.second_param = second
            self.is_func_legal_signal.emit(True)
        else:
            self.is_func_legal_signal.emit(False)

    def runRename(self, text):
        lines = text.split('\n')

        is_lines_contain_preview_line = self.is_lines_contain_preview_line(lines)
        if is_lines_contain_preview_line:
            self._runPreviewReplaceSrcRename(lines)
        else:
            self._runFuncRename(lines)

        # fix 执行完成origin_files发生了改变
        self.origin_files = os.listdir(self.root)
        self.origin_files = [file for file in self.origin_files if os.path.isfile(os.path.join(self.root, file))]
        self.origin_files.sort()

        self.done_signal.emit('run')

    def _runPreviewReplaceSrcRename(self, lines):
        # preview中替换
        src_file = ''
        idx, diff, rn_num = 0, 0, ''
        has_src_file = False
        for line in lines:
            if settings.SRC_SIGN in line:
                _, src_file = self.getOrderAndSrcFileFromSrcLine(line)
                has_src_file = True
            elif settings.DESC_SIGN in line:
                idx += 1
                preview_file = self.getPreviewFileFromPreviewLine(line)
                if has_src_file:
                    if src_file != preview_file:
                        diff += 1
                        self.rename(os.path.join(self.root, src_file), os.path.join(self.root, preview_file), run=True)
                        rn_num = str(diff).zfill(self.line_nums_max_bit)
                    current = str(idx).zfill(self.line_nums_max_bit)
                    rename_done_html = self.srcFileToSrcHtml(current, preview_file)
                    self.line_to_preview_signal.emit(rename_done_html, current, rn_num)
                    time.sleep(self.delay_sec)
                    has_src_file = False

    def _runFuncRename(self, lines):
        idx, diff, rn_num = 0, 0, ''
        for line in lines:
            idx += 1
            if settings.SRC_SIGN in line:
                order, file = self.getOrderAndSrcFileFromSrcLine(line)
                new_file = self.func(self.first_param, self.second_param, self.root, file, run=True)
                if new_file != file:
                    diff += 1
                    rn_num = str(diff).zfill(self.line_nums_max_bit)
                rename_done_html = self.srcFileToSrcHtml(order, new_file)
                self.line_to_preview_signal.emit(rename_done_html, order, rn_num)
                time.sleep(self.delay_sec)

    def srcLineToSrcHtml(self, line):
        # line -> file -> html
        # 接收源文件行 返回源文件名称,源文件line<html>预览
        src_order, src_file = self.getOrderAndSrcFileFromSrcLine(line)
        return src_file, self.srcFileToSrcHtml(src_order, src_file)

    def srcLineToPreviewHtml(self, line):
        # line -> file -> html
        # 接收源文件行 返回函数再处理的预览行
        order, src_file = self.getOrderAndSrcFileFromSrcLine(line)
        new_file = self.func(self.first_param, self.second_param, self.root, src_file, run=False)
        # 确保新文件名字不同才 显示为绿色
        if new_file == src_file:
            return False, self.previewFileToPreviewHtml(new_file)
        else:
            return True, self.previewFileToPreviewHtml(new_file, color='LimeGreen')

    def previewLineToPreviewHtml(self, line, src_file):
        # line -> preview file -> html
        # 接收预览行 返回函数再处理的预览行
        preview_file = self.getPreviewFileFromPreviewLine(line)
        new_file = self.func(self.first_param, self.second_param, self.root, preview_file, run=False)
        # 确保新文件名字不同才 显示为绿色
        if new_file == src_file:
            return False, self.previewFileToPreviewHtml(new_file)
        elif new_file != preview_file and preview_file != src_file:
            return True, self.previewFileToPreviewHtml(new_file, color='Green')
        else:
            return True, self.previewFileToPreviewHtml(new_file, color='LimeGreen')

    @staticmethod
    def is_lines_contain_preview_line(lines):
        is_contain_preview_line = False
        for line in lines:
            if settings.DESC_SIGN in line:
                is_contain_preview_line = True
                break
        return is_contain_preview_line

    @property
    def line_nums_max_bit(self):
        line_nums = len(self.origin_files)
        line_nums_str = str(line_nums)
        return len(line_nums_str)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 适配更高分辨率
    QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    window = QuickReName()
    sys.exit(app.exec())
